{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "PDR ResNet152V2 Testing.ipynb",
      "provenance": [],
      "collapsed_sections": [],
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/aldnoahh/plant-disease-recognition/blob/master/PDR_ResNet152V2_Testing.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "QeWLiMnG0_uu",
        "colab_type": "text"
      },
      "source": [
        "#Plant Disease Recognition using ResNet152V2 on modified version of PlantVillage Dataset."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Cj7pw9-JwAh3",
        "colab_type": "text"
      },
      "source": [
        "Importing necessary libraries"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "RZFTFusEcsec",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "import tensorflow as tf\n",
        "print(tf.__version__)"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "a4s4g3_Bcwu8",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "from tensorflow.keras.layers import Input, Dense, Flatten\n",
        "from tensorflow.keras.applications.resnet_v2 import ResNet152V2 as PretrainedModel, preprocess_input\n",
        "from tensorflow.keras.models import Model\n",
        "from tensorflow.keras.optimizers import SGD, Adam\n",
        "from tensorflow.keras.preprocessing import image\n",
        "from tensorflow.keras.preprocessing.image import ImageDataGenerator\n",
        "from tensorflow.keras.layers import BatchNormalization\n",
        "from glob import glob\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "import matplotlib.pyplot as plt\n",
        "import sys, os"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "zhXZ3Y9WwGdR",
        "colab_type": "text"
      },
      "source": [
        "Downloading and unzipping the modified dataset available on Gdrive. If you don`t have gdown module, install it using pip."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "EPxnx4LVc_fW",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#capture command here suppresses the large output\n",
        "%%capture\n",
        "!gdown --id 1Mj6wsKBZN2ycAyyIMs2lI361deuCJqBI --output pv0.zip\n",
        "!unzip pv0.zip"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ngDbRT7iwdJi",
        "colab_type": "text"
      },
      "source": [
        "Check if the folder has been unzipped."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "V7NhBksadZHd",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "!ls"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "jOLHCEL1wiUd",
        "colab_type": "text"
      },
      "source": [
        "Setting up path for datagenerators from keras"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "xu1nf5xkeiXs",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "train_path = '/content/pv0/train'\n",
        "valid_path = '/content/pv0/test'\n",
        "# useful for getting number of files\n",
        "image_files = glob(train_path + '/*/*.JPG')\n",
        "valid_image_files = glob(valid_path + '/*/*.JPG')\n",
        "# useful for getting number of classes\n",
        "folders = glob(train_path + '/*')\n",
        "len(folders)"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "a-eZ_Jwnw22H",
        "colab_type": "text"
      },
      "source": [
        "Specify input image size."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "pS1RfyFueujP",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "IMAGE_SIZE = [256, 256]"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Tt6ccnhhfJ6y",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#sneek peek at a random image\n",
        "plt.imshow(image.load_img(np.random.choice(image_files)))\n",
        "plt.show()"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "W6g4APyRxBvP",
        "colab_type": "text"
      },
      "source": [
        "Configuring the pretrainned model as per our needs."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Mz1weoVCfN0-",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "ptm = PretrainedModel(\n",
        "    input_shape=IMAGE_SIZE + [3],\n",
        "    weights='imagenet',\n",
        "    include_top=False)\n",
        "# freeze pretrained model weights\n",
        "ptm.trainable = False"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "YkxxS9zifoR3",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "K = len(folders) # number of classes\n",
        "\n",
        "#model definition\n",
        "x = Flatten()(ptm.output)\n",
        "x= BatchNormalization()(x)\n",
        "x= Dense(512,activation='relu')(x)\n",
        "x = Dense(K, activation='softmax')(x)"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "TqzghjxNftgX",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# create a model object\n",
        "model = Model(inputs=ptm.input, outputs=x)"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "U8EO7sLIft1r",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# view the structure of the model\n",
        "model.summary()"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "tYty5eS-U9gY",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#view the number of layers in the model\n",
        "len(model.layers)"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "wKC5ilt4hc1K",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# create an instance of ImageDataGenerator\n",
        "#Keras generators returns one-hot encoded labels and provides data augmentation.\n",
        "gen_train = ImageDataGenerator(\n",
        "  rotation_range=90,\n",
        "  width_shift_range=0.1,\n",
        "  height_shift_range=0.1,\n",
        "  shear_range=0.1,\n",
        "  zoom_range=0.2,\n",
        "  horizontal_flip=True,\n",
        "  preprocessing_function=preprocess_input\n",
        ")\n",
        "\n",
        "gen_test = ImageDataGenerator(\n",
        "  preprocessing_function=preprocess_input\n",
        ")"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "SgtuBIoThuZc",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#batch size is the number of examples that are run through the model at once.\n",
        "batch_size = 300\n",
        "\n",
        "# create generators\n",
        "train_generator = gen_train.flow_from_directory(\n",
        "  train_path,\n",
        "  shuffle=True,\n",
        "  target_size=IMAGE_SIZE,\n",
        "  batch_size=batch_size,\n",
        ")\n",
        "valid_generator = gen_test.flow_from_directory(\n",
        "  valid_path,\n",
        "  target_size=IMAGE_SIZE,\n",
        "  batch_size=batch_size,\n",
        ")"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "vXvEj-1byMah",
        "colab_type": "text"
      },
      "source": [
        "Since Keras no longer provides some metrics within itself, so we define those metrics ourselves. Here, we are defining F1_score, Precision and Recall."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "vRsYGkdM3cTR",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "from keras import backend as Ke\n",
        "\n",
        "def recall_m(y_true, y_pred):\n",
        "    true_positives = Ke.sum(Ke.round(Ke.clip(y_true * y_pred, 0, 1)))\n",
        "    possible_positives = Ke.sum(Ke.round(Ke.clip(y_true, 0, 1)))\n",
        "    recall = true_positives / (possible_positives + Ke.epsilon())\n",
        "    return recall\n",
        "\n",
        "def precision_m(y_true, y_pred):\n",
        "    true_positives = Ke.sum(Ke.round(Ke.clip(y_true * y_pred, 0, 1)))\n",
        "    predicted_positives = Ke.sum(Ke.round(Ke.clip(y_pred, 0, 1)))\n",
        "    precision = true_positives / (predicted_positives + Ke.epsilon())\n",
        "    return precision\n",
        "\n",
        "def f1_m(y_true, y_pred):\n",
        "    precision = precision_m(y_true, y_pred)\n",
        "    recall = recall_m(y_true, y_pred)\n",
        "    return 2*((precision*recall)/(precision+recall+Ke.epsilon()))"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3LvAj8U_yg7Y",
        "colab_type": "text"
      },
      "source": [
        "This block is for creating a lr scheduler, since the lr scheduler was not as effective as using adam directly, it is left for experimentation."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "j0MqLWki7xxg",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# from keras.optimizers import SGD\n",
        "# import math\n",
        "# def step_decay(epoch):\n",
        "# \tinitial_lrate = 1e-4\n",
        "# \tdrop = 0.5\n",
        "# \tepochs_drop = 10.0\n",
        "# \tlrate = initial_lrate * math.pow(drop, math.floor((1+epoch)/epochs_drop))\n",
        "# \treturn lrate\n",
        "# sgd = SGD(lr=0.0, momentum=0.9)\n",
        "# # learning schedule callback\n",
        "# from keras.callbacks import LearningRateScheduler\n",
        "# lrate = LearningRateScheduler(step_decay)\n",
        "# callbacks_list = [lrate]"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "a_JS4tg1y17w",
        "colab_type": "text"
      },
      "source": [
        "Compiling our model with loss, optimizer and metrics (including our custom defined ones)."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "I5pDP0IStBZp",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "model.compile(\n",
        "  loss='categorical_crossentropy',\n",
        "  optimizer='adam',\n",
        "  metrics=['accuracy',f1_m,precision_m, recall_m]\n",
        ")"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "EaUJj4D0y_VU",
        "colab_type": "text"
      },
      "source": [
        "The fit function is called for starting our training. \n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "rlwkypTAsqW2",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# fit the model\n",
        "r = model.fit(\n",
        "  train_generator,\n",
        "  validation_data=valid_generator,\n",
        "  epochs=5,\n",
        "  steps_per_epoch=int(np.ceil(len(image_files) / batch_size)),\n",
        "  validation_steps=int(np.ceil(len(valid_image_files) / batch_size)),\n",
        ")"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tM5UwYJXzLiL",
        "colab_type": "text"
      },
      "source": [
        "Saving our Model in HD5 format."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "dOhj9StKbLPC",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "model.save(\"model.h5\")\n",
        "print(\"Saved model to disk\")"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tL1gKZi_zSz2",
        "colab_type": "text"
      },
      "source": [
        "Graphs for our metrics"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "2oa3tHhOSC8J",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# loss\n",
        "plt.plot(r.history['loss'], label='train loss')\n",
        "plt.plot(r.history['val_loss'], label='val loss')\n",
        "plt.legend()\n",
        "plt.show()"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "T3urB6c6SDaU",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# accuracies\n",
        "plt.plot(r.history['accuracy'], label='train acc')\n",
        "plt.plot(r.history['val_accuracy'], label='val acc')\n",
        "plt.legend()\n",
        "plt.show()"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "-yt89FJw-_6M",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# f1_score\n",
        "plt.plot(r.history['f1_m'], label='train f1_m')\n",
        "plt.plot(r.history['val_f1_m'], label='val f1_m')\n",
        "plt.legend()\n",
        "plt.show()"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "KxkOwoIHFXJa",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# precision\n",
        "plt.plot(r.history['precision_m'], label='train precision_m')\n",
        "plt.plot(r.history['val_precision_m'], label='val precision_m')\n",
        "plt.legend()\n",
        "plt.show()"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "gd_sJAJ2FX0S",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# recall\n",
        "plt.plot(r.history['recall_m'], label='train recall_m')\n",
        "plt.plot(r.history['val_recall_m'], label='val recall_m')\n",
        "plt.legend()\n",
        "plt.show()"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "oATCaclAzgYy",
        "colab_type": "text"
      },
      "source": [
        "Next we evaluate the model on our test set again."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "KXJFLjk8tNad",
        "colab": {}
      },
      "source": [
        "#Load saved model from training\n",
        "from keras.models import load_model\n",
        "amod= load_model('/content/model.h5')\n",
        "# evaluate the model\n",
        "valid_generator = gen_test.flow_from_directory(valid_path,target_size=IMAGE_SIZE,batch_size=batch_size,)\n",
        "loss, accuracy, f1_score, precision, recall = amod.evaluate(valid_generator, steps=int(np.ceil(len(valid_image_files)/ batch_size)))"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "x7bbYfpCzoDw",
        "colab_type": "text"
      },
      "source": [
        "Printing our metrics"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "IB5Lnaf7tNak",
        "colab": {}
      },
      "source": [
        "print('loss     : ',loss)\n",
        "print('accuracy : ',accuracy)\n",
        "print('f1_score :',f1_score)\n",
        "print('precision:',precision)\n",
        "print('recall   :',recall)"
      ],
      "execution_count": null,
      "outputs": []
    }
  ]
}